// system.cc 
//    Nachos initialization and cleanup routines.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "system.h"
#include "syscall.h"
#include "list.h"
// This defines *all* of the global data structures used by Nachos.
// These are all initialized and de-allocated by this file.

Thread *currentThread;            // the thread we are running now
Thread *threadToBeDestroyed;      // the thread that just finished
Scheduler *scheduler;            // the ready list
Interrupt *interrupt;            // interrupt status
Statistics *stats;            // performance metrics
Timer *timer;                // the hardware timer device,
                    // for invoking context switches
;
#ifdef FILESYS_NEEDED
FileSystem  *fileSystem;
#endif


#ifdef FILESYS
SynchDisk   *synchDisk;
#endif

#ifdef USER_PROGRAM// requires either FILESYS or FILESYS_STUB
#include "bitmap.h"
#include "thread.h"
#include "addrspace.h"
BitMap * coremap;
List* active;
int sizeArray = 100;
SpaceId SpaceIdInt = 0;
int sizeCount =0;
bool testSpace = TRUE;

// creates bitmap for entire system
// jpj9668
Machine *machine;    // user program memory and registers
#endif

#ifdef NETWORK
PostOffice *postOffice;
#endif


// External definition, to allow us to take a pointer to this function
extern void Cleanup();

int *flag = new int;

//----------------------------------------------------------------------
// TimerInterruptHandler
//     Interrupt handler for the timer device.  The timer device is
//    set up to interrupt the CPU periodically (once every TimerTicks).
//    This routine is called each time there is a timer interrupt,
//    with interrupts disabled.
//
//    Note that instead of calling Yield() directly (which would
//    suspend the interrupt handler, not the interrupted thread
//    which is what we wanted to context switch), we set a flag
//    so that once the interrupt handler is done, it will appear as 
//    if the interrupted thread called Yield at the point it is 
//    was interrupted.
//
//    "dummy" is because every interrupt handler takes one argument,
//        whether it needs it or not.
//----------------------------------------------------------------------
static void
TimerInterruptHandler(int dummy)
{
    if (interrupt->getStatus() != IdleMode)
    interrupt->YieldOnReturn();
}

//----------------------------------------------------------------------
// Initialize
//     Initialize Nachos global data structures.  Interpret command
//    line arguments in order to determine flags for the initialization.  
// 
//    "argc" is the number of command line arguments (including the name
//        of the command) -- ex: "nachos -d +" -> argc = 3 
//    "argv" is an array of strings, one for each command line argument
//        ex: "nachos -d +" -> argv = {"nachos", "-d", "+"}
//----------------------------------------------------------------------
void
Initialize(int argc, char **argv)
{
    *flag = 0;
    int argCount;
    char* debugArgs = "";
    bool randomYield = FALSE;

#ifdef USER_PROGRAM
    sizeCount = 0;

    sizeArray = 100;
    
    coremap = new BitMap(32);
    active = new List();
    active->Remove();
    bool debugUserProg = FALSE;    // single step user program
#endif
#ifdef FILESYS_NEEDED
    bool format = FALSE;    // format disk
#endif
#ifdef NETWORK
    double rely = 1;        // network reliability
    int netname = 0;        // UNIX socket name
#endif
    for (argc--, argv++; argc > 0; argc -= argCount, argv += argCount) {
    
    //printf(argv);
    argCount = 1;
    if (!strcmp(*argv, "-d")) {
        if (argc == 1)
        debugArgs = "+";    // turn on all debug flags
        else {
            debugArgs = *(argv + 1);
            argCount = 2;
        }
    }
    /* if (!strcmp(*argv "-M"))
        SpaceId prog;
        if(!strcmp(argc[1], "1")
        {
            prog = Exec("../test/FirstFit");
        }
        else if(!strcmp(argc[1], "2")
        {
             prog = Exec("../test/BestFit");
        }
        else if(!strcmp(argc[1], "3")
        {
             prog = Exec("../testWorstFit");
        };
        else
        {
            prog = Exec("../test/FirstFit");
        }
    } */
    else if(!strcmp(*argv, "-A")) 
    {
        //printf("%d", argc);
        
            if (!strcmp(argv[1], "1"))
            {
                //I think you would fork a thread here
                //printf(" in system.cc flag == %d\n", *flag);
                *flag = 1;
                currentThread = new Thread("main");        
                currentThread->setStatus(RUNNING);
                //printf(" in system.cc flag == %d\n", *flag);
            }
            else if (!strcmp(argv[1], "2"))
            {
                *flag = 2;
                //printf(" in system.cc flag == %d\n", *flag);
                currentThread = new Thread("main");        
                currentThread->setStatus(RUNNING);
            }
            else if (!strcmp(argv[1], "3"))
            {
                *flag = 3;
                //printf(" in system.cc flag == %d\n", *flag);
                currentThread = new Thread("main");        
                currentThread->setStatus(RUNNING);
            }
            else if (!strcmp(argv[1], "4"))
            {
                *flag = 4;
                //printf(" in system.cc flag == %d\n", *flag);
                currentThread = new Thread("main");        
                currentThread->setStatus(RUNNING);
            }
            else if (!strcmp(argv[1], "5"))
            {
                *flag = 5;
                //printf(" in system.cc flag == %d\n", *flag);
                currentThread = new Thread("main");        
                currentThread->setStatus(RUNNING);
            }
            else
            {
                printf("Use 1 through 5 for arguments. \n");
                Cleanup();
            };
    }    
    else if (!strcmp(*argv, "-rs")) {
        ASSERT(argc > 1);
        RandomInit(atoi(*(argv + 1)));    // initialize pseudo-random
                        // number generator
        randomYield = TRUE;
        argCount = 2;
    }
#ifdef USER_PROGRAM
    if (!strcmp(*argv, "-s"))
        debugUserProg = TRUE;
#endif
#ifdef FILESYS_NEEDED
    if (!strcmp(*argv, "-f"))
        format = TRUE;
#endif
#ifdef NETWORK
    if (!strcmp(*argv, "-l")) {
        ASSERT(argc > 1);
        rely = atof(*(argv + 1));
        argCount = 2;
    } else if (!strcmp(*argv, "-m")) {
        ASSERT(argc > 1);
        netname = atoi(*(argv + 1));
        argCount = 2;
    }
#endif
    }

    DebugInit(debugArgs);            // initialize DEBUG messages
    stats = new Statistics();            // collect statistics
    interrupt = new Interrupt;            // start up interrupt handling
    scheduler = new Scheduler();        // initialize the ready queue
    if (randomYield)                // start the timer (if needed)
    timer = new Timer(TimerInterruptHandler, 0, randomYield);

    threadToBeDestroyed = NULL;

    // We didn't explicitly allocate the current thread we are running in.
    // But if it ever tries to give up the CPU, we better have a Thread
    // object to save its state. 
    currentThread = new Thread("main");        
    currentThread->setStatus(RUNNING);

    interrupt->Enable();
    CallOnUserAbort(Cleanup);            // if user hits ctl-C
    
#ifdef USER_PROGRAM
    machine = new Machine(debugUserProg);    // this must come first
#endif

#ifdef FILESYS
    synchDisk = new SynchDisk("DISK");
#endif

#ifdef FILESYS_NEEDED
    fileSystem = new FileSystem(format);
#endif

#ifdef NETWORK
    postOffice = new PostOffice(netname, rely, 10);
#endif


}

//----------------------------------------------------------------------
// Cleanup
//     Nachos is halting.  De-allocate global data structures.
//----------------------------------------------------------------------
void
Cleanup()
{
    printf("\nCleaning up...\n");
#ifdef NETWORK
    delete postOffice;
#endif
    
#ifdef USER_PROGRAM
    delete machine;
#endif

#ifdef FILESYS_NEEDED
    delete fileSystem;
#endif

#ifdef FILESYS
    delete synchDisk;
#endif
    
    delete timer;
    delete scheduler;
    delete interrupt;
    
    Exit(0);
}

